home *** CD-ROM | disk | FTP | other *** search
- { Inliner
-
- Version 1.00 File: INLINER.PAS
- Last revised: 12 Apr 1985 Author: Anthony M. Marcy
-
- DESCRIPTION
-
- Inliner is an assembler which translates 8088 assembly language directly
- into Turbo Pascal INLINE code. It is written in, and generates code for,
- Turbo Pascal 2.00 for the IBM PC. This program is in the public domain.
- Inliner accepts a source language similar, but not identical, to that
- of the IBM Macro Assembler (MASM). It produces a single Turbo INLINE statement
- ready to be merged into a Pascal program or used as an Include file.
- All 8088 instructions are supported. MASM pseudo-ops are not, and there
- are a few differences in syntax between Inliner and MASM, as detailed below.
- System requirements are those for running Turbo. If you can compile
- Inliner, you can run it. (If you can't compile it, you don't need it.)
- Maximum assembly program size is set by the size of memory. Inliner can use
- all available contiguous memory.
- The new version 3.00 of Turbo has changes to the INLINE statement which
- make it not always compatible with code written for Turbo 2.00. Inliner 1.00
- is designed to work with Turbo 2.00. In particular, assembly programs which
- contain both labels and constant identifiers, and assembled by Inliner, may
- not compile correctly under Turbo 3.00.
-
- GETTING STARTED
-
- You will be prompted for a source file and a target file. If no source
- filename extension is given, .ASM is assumed. The default target file is
- your source filename with extension .PAS; a carriage return accepts the
- default, or you may enter any legal filename.
- Quick trick: entering TRM: as the source file will allow you to type your
- input directly into Inliner. It will not be saved, however, and no editing
- is available. End your input with ctrl-z. Entering NUL as the target file
- will cause no output file to be generated, but you can still see the output
- on the screen. Handy if you just need a line or two, or for testing what
- will "work".
- Inliner may also be started from the DOS command line, thus:
- A> inliner infile.asm outfile.pas
- The second parameter may be omitted, in which case the default is assumed.
-
-
- INSTRUCTION FORMAT
-
- An Inliner source line takes the general form:
- label: opcode operand, operand ;comment
- Each of these components is optional.
-
- A LABEL can be anything that would be legal as a Turbo identifier, limited
- in length to a maximum of twenty characters. The colon is mandatory after
- a label.
-
- OPCODEs are the standard Intel mnemonics. LOCK and the various REP
- prefixes are supported. The segment override prefix can only be placed before
- an operand, not before the opcode.
-
- OPERANDs can be of three general kinds: register, address, and immediate.
- Register operands are the usual mnemonics - AX,BX, etc.
- Address operands have the following form:
- prefix: (type) [base] [index] offset
- Each component is optional. The ordering is strict.
- prefix is a segment override -- DS, CS, SS, or ES
- type is a single letter -- N Near
- F Far
- S Short
- W Word
- B Byte
- base is a base register -- BX or BP
- index is an index register -- SI or DI
- offset is either a literal constant or a Turbo identifier
-
- Turbo identifiers are copied into the INLINE code. Any identifier which does
- not occur as a label is assumed to be a Turbo identifier. The compiler replaces
- variable names with their offsets within their segments; it replaces constant
- identifiers with their values. The location counter, *, is also legal. See
- the Turbo manual for details.
- ADD AL,var1 ;var1 is a global variable in the data segment
- ADD AL,[BP]var2 ;var2 is a local variable in the stack segment
- ADD AL,CS:var3 ;var3 is a typed constant in the code segment
-
- Immediate operands are distinguished by being prefixed with an equal sign.
- They may be constants or Turbo variables. Thus,
- MOV AX,=2 ;loads the value 2 into AX
- MOV AX,2 ;loads AX with the word at offset 2 in the data segment
- MOV AX,var1 ;loads AX with the contents of variable var1
- MOV AX,=var1 ;loads the offset of variable var1 into AX
- The equal sign is optional in the INT, RET, IN, and OUT instructions, and
- before character literals.
-
- CONSTANTs can be decimal integers (positive or negative), hex constants
- in Turbo format (preceded by $), constant identifers, or character literals
- enclosed in single quotes. Examples: 2 -128 $FF cons 'x'
- The type must be specified when it cannot otherwise be deduced:
- ADD AX,[BP]2 ;AX - must be a word operand
- INC (W)[BP]2 ;requires (W) or (B)
- Immediate numeric constants default to (B)yte if in the range -128..255,
- otherwise (W)ord.
-
- JMP requires special treatment. A (F)ar jump to an absolute address may be
- coded with two operands, both immediate constants, representing the segment
- and the offset:
- JMP =$0060,=$0100 ;absolute address 0060:0100
- A (N)ear jump to an offset in the CS requires a single immediate operand:
- JMP =$0100 ;address CS:0100
- JMP =*-1 ;this instruction jumps to itself
- An indirect jump takes either a register or an address operand. In the latter
- case, the type must be specified:
- JMP AX ;must be (N)ear
- JMP (F)[BP][SI]
- JMP (N)var1
- Lastly, the jump target may be an Inliner label. For forward references,
- more efficient code can be generated if (S)hort is specified when possible:
- JMP lab1
- JMP (S)lab2
-
- CALL is similar to JMP, except that (S)hort cannot be used.
-
- The conditional jump instructions -- JE, JNE, etc. -- take a single
- operand which may be either an immediate constant in the range -128..127
- or an Inliner label.
-
- The string instructions vary slightly from MASM syntax. REP, REPZ, etc.,
- are considered prefixes which must be placed before a string opcode on the
- same line. The special no-operand forms of the string opcodes -- MOVSB,
- MOVSW, etc. -- are not implemented. Instead, use the basic opcode with
- a type specifier. The full two-operand forms may also be written.
- REP CMPS (B)
- REP MOVS (W)[SI],[DI]
-
- Other instructions resemble their counterparts in MASM. Refer to the
- Macro Assembler manual for their formats. Inliner does not support any
- pseudo-ops, such as PROC, END, DW, or ASSUME. Nor does it support the
- 8087 mnemonics.
- Pascal declarations should be used to define data, in place of DB, DW,
- EQU, etc. But remember that your variables are Turbo variables -- Inliner
- cannot see your declarations to check type or addressibility. You must
- provide segment overrides where needed.
-
-
- EXAMPLES
-
- Here are some more examples of Inliner code:
-
- PUSH BP
- h2: CMP var1,=-1 ;byte variable assumed
- CMP var1,(W)=-1 ;unless overridden
- MOV var2,=var4 ;address is always two bytes
- JE (S)h5
- REPE SCAS(B) ;instead of SCASB
- shl ax,cl ;lower case is OK
- ESC = 23 , [ DI ] var2 ;spaces are OK, too
- MOV ES:4,'&'
- h5: SUB (W)var3,=$40
- NOP
- CALL (N)xyz ;indirect through variable xyz
- ;unless xyz is a label
- MOV [BX][DI],CS
- RET (N) 4 ;(N) or (F) required
-
- -----------------------------------------------------------------
-
- Inliner is supported on the RBBS-PC operated by
- James Miles
- "The Programmer's Toolbox"
- (301) 540-7230 (data)
- 24 Hrs.
- Comments, bug reports, and suggested improvements are encouraged. Address
- them to ANTHONY MARCY or to SYSOP. If you make extensions or revisions
- to this program, please upload so that all may share.
-
- Enjoy!
-
- -----------------------------------------------------------------}
-
-
- program inliner;
-
- const
- tsize = 200; { size of symbol table }
-
- type
- filename = string[20];
- opcode = (nul,
- mov,push,pop,xchg,in_,out,xlat,lea,lds,les,lahf,sahf,pushf,
- popf,add,adc,inc,sub,sbb,dec,neg,cmp,aas,das,mul,imul,aam,div_,
- idiv,aad,cbw,cwd,not_,shl_,sal,shr_,sar,rol,ror,rcl,rcr,and_,
- test_,or_,xor_,aaa,daa,rep,repe,repz,repne,repnz,movs,cmps,scas,
- lods,stos,call,jmp,ret,je,jz,jl,jnge,jle,jng,jb,jnae,jbe,jna,jp,
- jpe,jo,js,jne,jnz,jnl,jge,jnle,jg,jnb,jae,jnbe,ja,jnp,jpo,jno,jns,
- loop,loopz,loope,loopnz,loopne,jcxz,int,into,iret,
- clc,cmc,stc,cld,std,cli,sti,hlt,wait,esc,lock,nop,
- valid,
- assume,comment,db,dd,dq,dt,dw,end_,equ,even,extrn,group,include,
- label_,name,org,proc,public,record_,segment,struc,macro,endm,
- page,subttl,title,
- fld,fst,fstp,fxch,fcom,fcomp,fcompp,ftst,fxam,fadd,fsub,fmul,fdiv,
- fsqrt,fscale,fprem,frndint,fxtract,fabs,fchs,fptan,fpatan,f2xm1,
- fyl2x,fyl2xp1,fldz,fld1,fldpi,fldl2t,fldl2e,fldlg2,fldln2,finit,
- feni,fdisi,fldcw,fstcw,fstsw,fclex,fstenv,fldenv,fsave,frstor,
- fincstp,fdecstp,ffree,fnop,fwait,
- last);
- regs = (firstreg,ax,bx,cx,dx,sp,bp,si,di,al,bl,cl,dl,ah,bh,ch,dh,
- ds,ss,cs,es,lastreg);
- line = string[80];
- idtype = string[20];
- attr = record { attributes of an operand }
- isop: boolean;
- isaddr: boolean;
- isid: boolean;
- isconst: boolean;
- value: integer;
- isreg: boolean;
- issreg: boolean;
- rg: regs;
- isimmed: boolean;
- isidx,isbase: boolean;
- idx,base: regs;
- isbyte,isword: boolean;
- isshort,isnear,isfar: boolean;
- ident: idtype;
- end;
- cptr = ^codrec;
- codrec = record { intermediate form of a line of code }
- next: cptr;
- labeln: integer;
- op: opcode;
- op1,op2: attr;
- repx: opcode;
- lockx: boolean;
- override: regs;
- source: line;
- errn: byte;
- end;
- charset = set of char;
-
- var
- reg: array[regs] of string[2]; { register mnemonics }
- rn: array[regs] of 0..7; { register numbers }
- mn: array[opcode] of string[6]; { opcode mnemonics }
- tab: array[0..tsize] of record { symbol table }
- id: idtype;
- val: integer;
- end;
- src,targ: text; { source and target files }
- errn,pass: byte; { error #, pass # }
- atstart,ok: boolean;
- t: string[132]; { target line }
- loc: integer; { location counter }
- tcnt: integer; { number of entries in symbol table }
- n: integer; { index into symbol table }
- oldlen: integer;
- firstentry: cptr; { points to first line of intermediate code }
- codpnt: cptr; { points to current line of intermediate code }
-
- op: opcode;
- op1,op2: attr;
- repx: opcode;
- lockx: boolean;
- override: regs;
-
-
- procedure error(j: integer); { only the first error in a line is recorded }
-
- begin
- if errn = 0 then errn := j;
- end;
-
- procedure message; { print error messages }
-
- begin
- if errn <> 0
- then begin
- ok := false;
- t := t + '***';
- case errn of
- 1: t := t + 'NOT ENOUGH OPERANDS';
- 2: t := t + 'INVALID OPERAND';
- 3: t := t + 'TYPE CONFLICT';
- 4: t := t + 'INVALID OPCODE';
- 5: t := t + 'INVALID REGISTER';
- 6: t := t + 'SYNTAX ERROR';
- 7: t := t + 'TYPE NOT SPECIFIED';
- 8: t := t + 'ILLEGAL REGISTER';
- 9: t := t + 'ERROR IN CONSTANT';
- 10: t := t + 'ILLEGAL OPERAND';
- 11: t := t + 'TOO MANY OPERANDS';
- 12: t := t + 'CONSTANT TOO BIG';
- 13: t := t + 'DUPLICATE PREFIX';
- 14: t := t + 'IDENTIFIER TOO LONG';
- 15: t := t + 'DUPLICATE LABEL';
- 16: t := t + 'UNDEFINED LABEL';
- 17: t := t + 'LABEL TOO FAR';
- 18: t := t + 'NOT IMPLEMENTED';
- { 29: system error }
-
- else t := t + 'SYSTEM ERROR';
- end;
- t := t + '***'
- end
- end;
-
- function stupcase(st: idtype): idtype;
-
- var i: integer;
-
- begin
- for i := 1 to length(st) do
- st[i] := upcase(st[i]);
- stupcase := st
- end; { stupcase }
-
- procedure startup; { input names of source and target files }
-
- var
- exists: boolean;
- inf,outf,tempstr: filename;
- commandline: string[127] absolute cseg:$80;
- params: string[127];
- default: byte;
-
- procedure chkinf; { does source file exist? }
- begin
- inf := stupcase(inf);
- if pos('.',inf) = 0
- then inf := inf + '.ASM';
- assign(src,inf);
- {$I-} reset(src) {$I+} ; { if so, open it }
- exists := (ioresult = 0);
- if pos(':',inf) = 0
- then inf := chr(default+65) + ':' + inf;
- if not exists
- then writeln('File ', inf, ' not found');
- end;
-
- procedure chkoutf; { is target filename valid? }
- begin
- outf := stupcase(outf);
- assign(targ,outf);
- {$I-} rewrite(targ) {$I+} ; { if so, open it }
- exists := (ioresult = 0);
- if pos(':',outf) = 0
- then outf := chr(default+65) + ':' + outf;
- if not exists
- then writeln('can''t open file ',outf);
- end;
-
- begin
- inf := ''; outf := ''; params := commandline;
- Inline(
- $B4/$19 { MOV AH,=$19 }
- /$CD/$21 { INT =$21 }
- /$88/$86/default ); { MOV [BP]default,AL }
- while (params <> '') and (params[1] = ' ') do
- delete(params,1,1);
- if params <> ''
- then begin { command line parameters }
- while (params <> '') and (params[1] <> ' ') do begin
- inf := inf + params[1];
- delete(params,1,1); end;
- chkinf;
- if not exists then begin
- commandline := '';
- startup; end
- else begin
- writeln('Source file: ',inf);
- while (params <> '') and (params[1] = ' ') do
- delete(params,1,1);
- if params <> ''
- then while (params <> '') and (params[1] <> ' ') do begin
- outf := outf + params[1];
- delete(params,1,1); end
- else outf := copy(inf,1,pos('.',inf)) + 'PAS';
- chkoutf;
- if not exists then begin
- commandline := '';
- startup; end
- else writeln('Target file: ',outf);
- end;
- end
- else begin { prompt for filenames }
- repeat
- write(' Source file [.ASM] ? '); readln(inf);
- chkinf;
- until exists;
- tempstr := copy(inf,1,pos('.',inf)) + 'PAS';
- repeat
- repeat
- write(' Target file [',tempstr,'] ? ');
- readln(outf); outf := stupcase(outf);
- until inf <> outf;
- if outf = '' then outf := tempstr;
- chkoutf;
- until exists;
- end;
- writeln;
- end; { startup }
-
- procedure init; { initialize tables }
-
- begin
- mn[mov ] := 'MOV' ; mn[push] := 'PUSH'; mn[pop ] := 'POP' ;
- mn[xchg] := 'XCHG'; mn[in_ ] := 'IN' ; mn[out ] := 'OUT' ;
- mn[xlat] := 'XLAT'; mn[lea ] := 'LEA' ; mn[lds ] := 'LDS' ;
- mn[les ] := 'LES' ; mn[lahf] := 'LAHF'; mn[pushf] := 'PUSHF';
- mn[sahf] := 'SAHF'; mn[popf] := 'POPF'; mn[add ] := 'ADD' ;
- mn[adc ] := 'ADC' ; mn[inc ] := 'INC' ; mn[sub ] := 'SUB' ;
- mn[sbb ] := 'SBB' ; mn[dec ] := 'DEC' ; mn[cmp ] := 'CMP' ;
- mn[aas ] := 'AAS' ; mn[das ] := 'DAS' ; mn[mul ] := 'MUL' ;
- mn[imul] := 'IMUL'; mn[aam ] := 'AAM' ; mn[div_] := 'DIV' ;
- mn[idiv] := 'IDIV'; mn[aad ] := 'AAD' ; mn[cbw ] := 'CBW' ;
- mn[cwd ] := 'CWD' ; mn[aaa ] := 'AAA' ; mn[daa ] := 'DAA' ;
- mn[not_] := 'NOT' ; mn[shl_] := 'SHL' ; mn[sal ] := 'SAL' ;
- mn[shr_] := 'SHR' ; mn[sar ] := 'SAR' ; mn[rol ] := 'ROL' ;
- mn[ror ] := 'ROR' ; mn[rcl ] := 'RCL' ; mn[rcr ] := 'RCR' ;
- mn[and_] := 'AND' ; mn[or_ ] := 'OR' ; mn[test_] := 'TEST';
- mn[xor_] := 'XOR' ; mn[rep ] := 'REP' ; mn[repne] := 'REPNE';
- mn[repe] := 'REPE'; mn[repz] := 'REPZ'; mn[repnz] := 'REPNZ';
- mn[movs] := 'MOVS'; mn[neg ] := 'NEG' ; mn[nop ] := 'NOP' ;
- mn[cmps] := 'CMPS'; mn[scas] := 'SCAS'; mn[lods] := 'LODS';
- mn[stos] := 'STOS'; mn[call] := 'CALL'; mn[jmp ] := 'JMP' ;
- mn[ret ] := 'RET' ; mn[je ] := 'JE' ; mn[jz ] := 'JZ' ;
- mn[jl ] := 'JL' ; mn[jnge] := 'JNGE'; mn[jle ] := 'JLE' ;
- mn[jng ] := 'JNG' ; mn[jb ] := 'JB' ; mn[jnae] := 'JNAE';
- mn[jbe ] := 'JBE' ; mn[jna ] := 'JNA' ; mn[jp ] := 'JP' ;
- mn[jpe ] := 'JPE' ; mn[jo ] := 'JO' ; mn[js ] := 'JS' ;
- mn[jne ] := 'JNE' ; mn[jnz ] := 'JNZ' ; mn[jnl ] := 'JNL' ;
- mn[jge ] := 'JGE' ; mn[jnle] := 'JNLE'; mn[jg ] := 'JG' ;
- mn[jnb ] := 'JNB' ; mn[jae ] := 'JAE' ; mn[jnbe] := 'JNBE';
- mn[ja ] := 'JA' ; mn[jnp ] := 'JNP' ; mn[jpo ] := 'JPO' ;
- mn[jno ] := 'JNO' ; mn[jns ] := 'JNS' ; mn[loopz ] := 'LOOPZ' ;
- mn[loop] := 'LOOP'; mn[jcxz] := 'JCXZ'; mn[loopnz] := 'LOOPNZ';
- mn[int ] := 'INT' ; mn[into] := 'INTO'; mn[loope ] := 'LOOPE' ;
- mn[iret] := 'IRET'; mn[clc ] := 'CLC' ; mn[loopne] := 'LOOPNE';
- mn[cmc ] := 'CMC' ; mn[stc ] := 'STC' ; mn[cld ] := 'CLD' ;
- mn[std ] := 'STD' ; mn[cli ] := 'CLI' ; mn[sti ] := 'STI' ;
- mn[hlt ] := 'HLT' ; mn[wait] := 'WAIT'; mn[esc ] := 'ESC' ;
- mn[lock] := 'LOCK';
- mn[valid] := '';
- mn[db ] := 'DB' ; mn[assume ] := 'ASSUME' ;
- mn[dd ] := 'DD' ; mn[comment] := 'COMMENT';
- mn[dq ] := 'DQ' ; mn[extrn ] := 'EXTRN' ;
- mn[dt ] := 'DT' ; mn[group ] := 'GROUP' ;
- mn[dw ] := 'DW' ; mn[include] := 'INCLUDE';
- mn[end_] := 'END' ; mn[label_ ] := 'LABEL' ;
- mn[equ ] := 'EQU' ; mn[public ] := 'PUBLIC' ;
- mn[even] := 'EVEN'; mn[record_] := 'RECORD' ;
- mn[name] := 'NAME'; mn[segment] := 'SEGMENT';
- mn[org ] := 'ORG' ; mn[struc ] := 'STRUC' ;
- mn[proc] := 'PROC'; mn[macro ] := 'MACRO' ;
- mn[endm] := 'ENDM'; mn[subttl ] := 'SUBTTL' ;
- mn[page] := 'PAGE'; mn[title ] := 'TITLE' ;
- mn[fld ] := 'FLD' ; mn[fst ] := 'FST' ; mn[fstp ] := 'FSTP' ;
- mn[fxch ] := 'FXCH' ; mn[fcom ] := 'FCOM' ; mn[fcomp ] := 'FCOMP' ;
- mn[fcompp] := 'FCOMPP'; mn[ftst ] := 'FTST' ; mn[fxam ] := 'FXAM' ;
- mn[fadd ] := 'FADD' ; mn[fsub ] := 'FSUB' ; mn[fmul ] := 'FMUL' ;
- mn[fdiv ] := 'FDIV' ; mn[fsqrt ] := 'FSQRT' ; mn[fscale] := 'FSCALE';
- mn[fprem ] := 'FPREM' ; mn[fabs ] := 'FABS' ; mn[frndint] := 'FRNDINT';
- mn[fchs ] := 'FCHS' ; mn[fptan ] := 'FPTAN' ; mn[fxtract] := 'FXTRACT';
- mn[fpatan] := 'FPATAN'; mn[f2xm1 ] := 'F2XM1' ; mn[fyl2x ] := 'FYL2X' ;
- mn[fldz ] := 'FLDZ' ; mn[fld1 ] := 'FLD1' ; mn[fyl2xp1] := 'FYL2XP1';
- mn[fldpi ] := 'FLDPI' ; mn[fldl2t] := 'FLDL2T'; mn[fldl2e] := 'FLDL2E';
- mn[fldlg2] := 'FLDLG2'; mn[fldln2] := 'FLDLN2'; mn[finit ] := 'FINIT' ;
- mn[feni ] := 'FENI' ; mn[fdisi ] := 'FDISI' ; mn[fldcw ] := 'FLDCW' ;
- mn[fstcw ] := 'FSTCW' ; mn[fstsw ] := 'FSTSW' ; mn[fclex ] := 'FCLEX' ;
- mn[fstenv] := 'FSTENV'; mn[fldenv] := 'FLDENV'; mn[fsave ] := 'FSAVE' ;
- mn[frstor] := 'FRSTOR'; mn[ffree ] := 'FFREE' ; mn[fincstp] := 'FINCSTP';
- mn[fnop ] := 'FNOP' ; mn[fwait ] := 'FWAIT' ; mn[fdecstp] := 'FDECSTP';
-
- reg[ax] := 'AX'; reg[bx] := 'BX'; reg[cx] := 'CX'; reg[dx] := 'DX';
- reg[sp] := 'SP'; reg[bp] := 'BP'; reg[si] := 'SI'; reg[di] := 'DI';
- reg[al] := 'AL'; reg[bl] := 'BL'; reg[cl] := 'CL'; reg[dl] := 'DL';
- reg[ah] := 'AH'; reg[bh] := 'BH'; reg[ch] := 'CH'; reg[dh] := 'DH';
- reg[ds] := 'DS'; reg[ss] := 'SS'; reg[cs] := 'CS'; reg[es] := 'ES';
- rn[ax] := 0; rn[bx] := 3; rn[cx] := 1; rn[dx] := 2;
- rn[sp] := 4; rn[bp] := 5; rn[si] := 6; rn[di] := 7;
- rn[al] := 0; rn[bl] := 3; rn[cl] := 1; rn[dl] := 2;
- rn[ah] := 4; rn[bh] := 7; rn[ch] := 5; rn[dh] := 6;
- rn[ds] := 3; rn[ss] := 2; rn[cs] := 1; rn[es] := 0;
- end; { init }
-
- function search(symbol: idtype): boolean; { search symbol table }
- begin { return index in global n }
- n := 0;
- symbol := stupcase(symbol);
- while (tab[n].id <> symbol) and (n <= tcnt) do n := n+1;
- if n = tcnt+1
- then search := false
- else search := true;
- end;
-
- procedure generate; { pass 2 -- maintain location counter }
- { pass 3 -- generate object code }
- var
- q0,w,md,rm: byte;
- q1: integer;
-
- procedure oneop; { test for exactly one operand }
- begin
- if op2.isop then error(11);
- if not op1.isop then error(1);
- end;
-
- procedure emit(q:byte); { emit one byte }
- function hex(d:byte): char;
- begin
- if d <= 9
- then hex := chr(48+d)
- else hex := chr(55+d);
- end;
- begin
- loc := loc+1;
- if (pass=3) and (errn = 0) then begin
- if atstart then t := t+' ' else t := t+'/';
- atstart := false;
- t := t+'$'+hex(q shr 4)+hex(q and 15);
- end;
- end;
-
- procedure emit2(q:integer); { emit two bytes }
- begin
- begin
- emit(q and $ff);
- emit(q shr 8);
- end
- end;
-
- procedure emitid(ident: idtype); { emit identifier }
- begin
- loc := loc+2;
- if (pass=3) and (errn = 0) then t := t+'/'+ident;
- end;
-
- procedure emitimm(op:attr); { emit immediate value }
- begin
- with op do
- if isid then emitid(ident)
- else if isconst then if (w=1) then emit2(value) else emit(value)
- else error(10);
- end;
-
- procedure checktype(op1,op2:attr); { check compatibility of operands }
- begin
- if (op1.isword and not op2.isbyte) or (op2.isword and not op1.isbyte)
- then w := 1
- else if (op1.isbyte and not op2.isword) or (op2.isbyte and not op1.isword)
- then w := 0
- else if not (op1.isbyte or op1.isword or op2.isbyte or op2.isword)
- then error(7)
- else error(3);
- if op1.issreg or op2.issreg then w := 0;
- end;
-
- procedure modrm(q:byte; op:attr); { construct the modregr/m byte }
- begin
- with op do begin
- if isid then md := 2
- else if isconst
- then if (value <= 127) and (value >= -128) then md := 1 else md := 2
- else md := 0;
-
- if isidx and isbase
- then begin
- if base = bx then rm := 0 else rm := 2;
- if idx = di then rm := rm+1;
- end
- else if not isidx and not isbase
- then begin
- md := 0; rm := 6; end
- else begin
- rm := 4;
- if isidx and (idx = di) then rm := rm+1;
- if isbase
- then if base = bp then rm := rm+2 else rm := rm+3;
- end;
- emit((md shl 6)+(q shl 3)+rm);
- if isid then emitid(ident);
- if isconst then begin
- if (value <= 127) and (value >= -128) then begin
- emit(value);
- if (md=0) and (rm=6) then if value<0 then emit($ff) else emit(0);
- end
- else emit2(value);
- end;
- end; end;
-
- procedure regtoreg(q:byte; op1,op2:attr);
- begin
- checktype(op1,op2);
- emit(q+w);
- emit(192 + (rn[op1.rg] shl 3) + rn[op2.rg]);
- end;
-
- procedure imtoacc(q:byte; op1,op2:attr);
- begin
- checktype(op1,op2);
- emit(q+w);
- emitimm(op2);
- end;
-
- procedure imtoreg(q:byte; op1,op2:attr);
- begin
- if op1.isword and op2.isbyte then w := 1 else checktype(op1,op2);
- emit(q+(w shl 3)+rn[op1.rg]);
- emitimm(op2);
- end;
-
- procedure onerm(q:byte; op:attr);
- begin
- with op do begin
- if isreg
- then emit(192+(q shl 3)+rn[rg])
- else if isaddr then modrm(q,op)
- else error(10);
- end;
- end;
-
- procedure imtorm(q,r:byte; op1,op2:attr; ext:boolean);
- begin
- if op1.isbyte and op2.isword then error(3)
- else if op1.isbyte and op2.isbyte then w := 0
- else if op1.isword and op2.isword then w := 1
- else if op1.isword and op2.isbyte then if ext then w := 3 else w := 1
- else if op1.isaddr and op2.isbyte then w := 0
- else if op1.isaddr and op2.isword then w := 1
- else error(29);
- emit(q+w);
- onerm(r,op1);
- emitimm(op2);
- end;
-
- procedure regmem(q: byte; op1,op2: attr);
- begin
- checktype(op1,op2);
- emit(q+w);
- modrm(rn[op1.rg],op2);
- end;
-
- procedure inout(q:byte; op1,op2:attr);
- begin
- if not (op1.isreg and (op1.rg in [ax,al])) then error(10);
- if op1.rg=ax then w := 1 else w := 0;
- if op2.isconst then begin
- if op2.isidx or op2.isbase then error(10);
- if (op2.value < 0) or (op2.value > 255) then error(12);
- emit(q+w);
- emit(op2.value);
- end
- else if op2.isreg and (op2.rg=dx) then emit(q+8+w)
- else error(10);
- end;
-
- begin { generate }
- t := ''; errn := codpnt^.errn;
- op1 := codpnt^.op1; op2 := codpnt^.op2;
- with codpnt^ do begin
- if errn=0 then begin
- if repx in [rep,repne,repnz] then emit($f2);
- if repx in [repe,repz] then emit($f3);
- if lockx then emit($f0);
- if override in [ds,cs,ss,es] then emit($26+(rn[override] shl 3));
-
- case op of
-
- nul: ;
-
- mov: begin
- w := 1;
- if not (op1.isop and op2.isop)
- then error(1)
- else if op1.issreg then begin
- if op1.rg=cs then error(10);
- q0 := $8e;
- if op2.isreg then regtoreg(q0,op1,op2)
- else if op2.isaddr then regmem(q0,op1,op2)
- else error(10);
- end
- else if op2.issreg then begin
- q0 := $8c;
- if op1.isreg then regtoreg(q0,op2,op1)
- else if op1.isaddr then regmem(q0,op2,op1)
- else error(10);
- end
- else if op2.isimmed then begin
- if op1.isreg
- then imtoreg($b0,op1,op2)
- else imtorm($c6,0,op1,op2,false);
- end
- else if op1.isreg and (op1.rg in [ax,al]) and op2.isaddr
- and not op2.isbase and not op2.isidx then begin
- if op1.rg = ax then emit($a1) else emit($a0);
- emitimm(op2);
- end
- else if op2.isreg and (op2.rg in [ax,al]) and op1.isaddr
- and not op1.isbase and not op1.isidx then begin
- if op2.rg = ax then emit($a3) else emit($a2);
- emitimm(op1);
- end
- else if op1.isreg and op2.isreg then begin
- q0 := $8a;
- regtoreg(q0,op1,op2); end
- else if (op1.isreg and op2.isaddr) or (op1.isaddr and op2.isreg)
- then begin
- q0 := $88;
- if op1.isaddr
- then regmem(q0,op2,op1)
- else begin
- q0 := q0+2;
- regmem(q0,op1,op2)
- end
- end
- else error(10);
- end;
-
- add,adc,sub,sbb,cmp,and_,or_,xor_,test_:
- begin
- if not (op1.isop and op2.isop)
- then error(1)
- else
- if op2.isimmed
- then if op1.isreg and ((op1.rg=ax) or (op1.rg=al))
- then begin
- if op1.isword then op2.isbyte := false;
- case op of
- add: q0 := $04;
- adc: q0 := $14;
- sub: q0 := $2c;
- sbb: q0 := $1c;
- cmp: q0 := $3c;
- and_: q0 := $24;
- or_ : q0 := $0c;
- xor_: q0 := $34;
- test_: q0 := $a8;
- end;
- imtoacc(q0,op1,op2);
- end
- else begin
- q0 := $80;
- case op of
- add: q1 := 0;
- adc: q1 := 2;
- sub: q1 := 5;
- sbb: q1 := 3;
- cmp: q1 := 7;
- and_: q1 := 4;
- or_ : q1 := 1;
- xor_: q1 := 6;
- test_: begin q0 := $f6; q1 := 0; end;
- end;
- if op in [add,adc,sub,sbb,cmp]
- then imtorm(q0,q1,op1,op2,true)
- else imtorm(q0,q1,op1,op2,false);
- end
-
- else if op1.isreg and op2.isreg
- then begin
- case op of
- add: q0 := $02;
- adc: q0 := $12;
- sub: q0 := $2a;
- sbb: q0 := $1a;
- cmp: q0 := $3a;
- and_: q0 := $22;
- or_ : q0 := $0a;
- xor_: q0 := $32;
- test_: q0 := $84;
- end;
- regtoreg(q0,op1,op2);
- end
- else if (op1.isaddr and op2.isreg) or (op1.isreg and op2.isaddr)
- then begin
- case op of
- add: q0 := $00;
- adc: q0 := $10;
- sub: q0 := $28;
- sbb: q0 := $18;
- cmp: q0 := $38;
- and_: q0 := $20;
- or_ : q0 := $08;
- xor_: q0 := $30;
- test_: q0 := $84;
- end;
- if op1.isaddr
- then regmem(q0,op2,op1)
- else begin
- if op<>test_ then q0 := q0+2;
- regmem(q0,op1,op2)
- end
- end
- else error(10);
- end;
-
- push,pop:
- begin
- with op1 do begin
- oneop;
- if issreg then begin
- if (op=pop) and (rg=cs) then error(10);
- case op of
- push: q0 := $06;
- pop: q0 := $07;
- end;
- emit(q0+(rn[rg] shl 3));
- end
- else if isreg then begin
- if not isword then error(3);
- case op of
- push: q0 := $50;
- pop: q0 := $58;
- end;
- emit(q0+rn[rg]);
- end
- else if isaddr then begin
- if isbyte then error(3);
- case op of
- push: begin q0 := $ff; q1 := 6; end;
- pop: begin q0 := $8f; q1 := 0; end;
- end;
- emit(q0);
- onerm(q1,op1);
- end
- else error(10);
- end;
- end;
-
- inc,dec:
- begin
- with op1 do begin
- oneop;
- if isreg and isword then begin
- case op of
- inc: q0 := $40;
- dec: q0 := $48;
- end;
- emit(q0+rn[rg]);
- end
- else if isaddr or isreg then begin
- if isbyte then w := 0
- else if isword then w := 1
- else error(7);
- case op of
- inc: q1 := 0;
- dec: q1 := 1;
- end;
- emit($fe+w);
- onerm(q1,op1);
- end
- else error(10);
- end;
- end;
-
- xchg:
- begin
- if not op2.isop then error(1);
- if op1.isreg and op2.isreg and ((op1.rg=ax) or (op2.rg=ax))
- then begin
- if op1.rg<>ax
- then emit($90+rn[op1.rg])
- else emit($90+rn[op2.rg]);
- end
- else if op1.isreg and op2.isreg
- then regtoreg($86,op1,op2)
- else if op1.isreg and op2.isaddr
- then regmem($86,op1,op2)
- else if op1.isaddr and op2.isreg
- then regmem($86,op2,op1)
- else error(10);
- end;
-
- mul,imul,div_,idiv,neg,not_:
- begin
- oneop;
- if op1.isbyte then q0 := $f6
- else if op1.isword then q0 := $f7
- else error(7);
- case op of
- mul: q1 := 4;
- imul: q1 := 5;
- div_: q1 := 6;
- idiv: q1 := 7;
- neg: q1 := 3;
- not_: q1 := 2;
- end;
- emit(q0);
- onerm(q1,op1);
- end;
-
- in_: inout($e4,op1,op2);
- out: inout($e6,op2,op1);
-
- lea,lds,les:
- begin
- if not op2.isop then error(1);
- if not(op1.isreg and op1.isword and op2.isaddr) then error(10);
- case op of
- lea: q0 := $8d;
- lds: q0 := $c5;
- les: q0 := $c4;
- end;
- emit(q0);
- onerm(rn[op1.rg],op2);
- end;
-
- shl_,sal,shr_,sar,rol,ror,rcl,rcr:
- begin
- with op2 do begin
- if not isop then error(1);
- if isidx or isbase then error(10);
- if isconst and (value=1) then q0 := $d0
- else if isreg and (rg=cl) then q0 := $d2
- else error(10);
- case op of
- shl_,sal: q1 := 4;
- shr_: q1 := 5;
- sar: q1 := 7;
- rol: q1 := 0;
- ror: q1 := 1;
- rcl: q1 := 2;
- rcr: q1 := 3;
- end;
- if op1.isword
- then q0 := q0+1
- else if not op1.isbyte then error(7);
- if not(op1.isreg or op1.isaddr) then error(10);
- emit(q0);
- onerm(q1,op1);
- end;
- end;
-
- lods,stos,scas:
- begin
- with op1 do begin
- if op2.isop then error(11);
- if not op1.isop then error(7);
- case op of
- lods: q0 := $ac;
- stos: q0 := $aa;
- scas: q0 := $ae;
- end;
- if isword then q0 := q0+1 else if not isbyte then error(7);
- if isbase or isimmed or isreg then error(10);
- if isidx and (((idx=si) and (op in [stos,scas]))
- or ((idx=di) and (op=lods))) then error(10);
- emit(q0);
- end; end;
-
- movs,cmps:
- begin
- if op2.isop then begin
- checktype(op1,op2);
- if op2.isidx and (((op2.idx=di) and (op=movs))
- or ((op2.idx=si) and (op=cmps))) then error(10);
- if op2.isbase or op2.isimmed or op2.isreg then error(10);
- end
- else if op1.isop then begin
- if op1.isword then w := 1
- else if op1.isbyte then w := 0
- else error(7);
- if op1.isimmed or op1.isreg or op1.isaddr then error(10);
- end
- else error(7);
- if op1.isop then begin
- if op1.isbase or op1.isimmed or op1.isreg then error(10);
- if op1.isidx and (((op1.idx=si) and (op=movs))
- or ((op1.idx=di) and (op=cmps))) then error(10);
- end;
- case op of
- movs: emit($a4+w);
- cmps: emit($a6+w);
- end;
- end;
-
- ret:
- begin
- if op2.isop then error(11);
- if not op1.isop then error(1);
- with op1 do begin
- if isidx or isbase or isreg or isid then error(10);
- if isconst then q0 := $c2 else q0 := $c3;
- if isfar then q0 := q0+8
- else if not isnear
- then if isshort then error(10) else error(7);
- emit(q0);
- if isconst then emit2(value);
- end
- end;
-
- jmp,call:
- begin
- with op1 do begin
- w := 1;
- if op2.isop then begin
- if not (isimmed and op2.isimmed) then error(10);
- if isnear or op2.isnear then error(3);
- case op of
- jmp: emit($ea);
- call: emit($9a);
- end;
- emitimm(op1);
- emitimm(op2);
- end
- else if not op1.isop then error(1)
- else if isfar then begin
- if (not isaddr) or (isid and search(ident)) then error(10);
- emit($ff);
- case op of
- jmp: onerm(5,op1);
- call: onerm(3,op1);
- end;
- end
- else if isimmed and isconst then begin
- if (value<=127) and (value>=-128) and (op=jmp)
- then begin emit($eb); emit(value); end
- else begin
- case op of
- jmp: emit($e9);
- call: emit($e8);
- end;
- emitimm(op1); end;
- end
- else if isid and search(ident) then begin
- if isidx or isbase then error(2);
- q1 := tab[n].val-loc-2;
- if pass=3 then begin
- if (op=jmp) and (q1 >= -128) and (q1 <= 127)
- then begin
- emit($eb);
- if isshort then emit(q1)
- else begin emit(q1); emit($90); end;
- end
- else begin
- case op of
- jmp: begin
- if isshort then error(17);
- emit($e9); end;
- call: begin
- if isshort then error(10);
- emit($e8); end;
- end;
- emit2(q1-1);
- end;
- end
- else begin {pass2}
- if (op=jmp) and (isshort or ((tab[n].val > -1) and (q1 > -128)))
- then begin emit2(0); isshort := true; end
- else begin emit2(0); emit(0); end;
- end;
- end
- else if (isreg or isaddr) and not (isbyte or isshort) then begin
- if not (isnear or isreg) then error(7);
- emit($ff);
- case op of
- jmp: onerm(4,op1);
- call: onerm(2,op1);
- end;
- end
- else error(10);
- end;
- end;
-
- je,jz,jl,jnge,jle,jng,jb,jnae,jbe,jna,jp,jpe,jo,js,jne,jnz,jnl,jge,jnle,
- jg,jnb,jae,jnbe,ja,jnp,jpo,jno,jns,loop,loopz,loope,loopnz,loopne,jcxz:
- begin
- oneop;
- with op1 do begin
- if (isimmed and isconst)
- then if not ((value>=-128) and (value<=127)) then error(12) else
- else if not (isid and not (isidx or isbase)) then error(10);
- case op of
- je,jz: q0 := $74;
- jl,jnge: q0 := $7c;
- jle,jng: q0 := $7e;
- jb,jnae: q0 := $72;
- jbe,jna: q0 := $76;
- jp,jpe: q0 := $7a;
- jo: q0 := $70;
- js: q0 := $78;
- jne,jnz: q0 := $75;
- jnl,jge: q0 := $7d;
- jnle,jg: q0 := $7f;
- jnb,jae: q0 := $73;
- jnbe,ja: q0 := $77;
- jnp,jpo: q0 := $7b;
- jno: q0 := $71;
- jns: q0 := $79;
- loop: q0 := $e2;
- loopz,loope: q0 := $e1;
- loopnz,loopne: q0 := $e0;
- jcxz: q0 := $e3;
- end;
- if isconst
- then begin emit(q0); emit(value); end
- else begin
- if (pass=3) and not search(ident) then error(16);
- q1 := tab[n].val-loc-2;
- if (pass=3) and ((q1 < -128) or (q1 > 127)) then error(17);
- emit(q0);
- emit(q1);
- end;
- end;
- end;
-
- int:
- begin
- with op1 do begin
- oneop;
- if isidx or isbase or not isconst then error(10);
- if (value < 0) or (value > 255) then error(12);
- if value=3 then emit($cc)
- else begin emit($cd); emit(value); end;
- end;
- end;
-
- esc:
- begin
- if not op2.isop then error(1);
- if not op1.isimmed then error(10);
- if (op1.value < 0) or (op1.value > 63) then error(10);
- emit($d8+(op1.value shr 3));
- onerm((op1.value and 7),op2);
- end;
-
- xlat,lahf,sahf,pushf,popf,aaa,daa,aas,das,cbw,cwd,into,iret,clc,cmc,
- stc,cld,std,cli,sti,hlt,wait,aam,aad,nop:
- begin
- if op1.isop then error(11);
- case op of
- xlat: emit($d7);
- lahf: emit($9f);
- sahf: emit($9e);
- pushf:emit($9c);
- popf: emit($9d);
- aaa: emit($37);
- daa: emit($27);
- aas: emit($3f);
- das: emit($2f);
- cbw: emit($98);
- cwd: emit($99);
- into: emit($ce);
- iret: emit($cf);
- clc: emit($f8);
- cmc: emit($f5);
- stc: emit($f9);
- cld: emit($fc);
- std: emit($fd);
- cli: emit($fa);
- sti: emit($fb);
- hlt: emit($f4);
- wait: emit($9b);
- aam: begin emit($d4); emit($0a); end;
- aad: begin emit($d5); emit($0a); end;
- nop: emit($90);
- end;
- end;
-
- else error(29);
- end; { case op }
- end; { if errn }
-
- if pass=3 then begin { finish constructing the target line }
- if codpnt = firstentry
- then begin
- writeln(targ,'Inline(');
- writeln; writeln('Inline('); end;
- message;
- if next = nil then t := t + ' );';
- while length(t) < 25 do t := t+' ';
- t := t + ' { ' + source;
- if length(t) < oldlen-4 { make it look pretty }
- then begin
- if length(t) > oldlen-8 then oldlen := oldlen+2;
- while length(t) < oldlen-4 do t := t+' ';
- end;
- t := t+' }';
- oldlen := length(t);
- writeln(targ,t); writeln(t); { and write it to the file }
- codpnt := next;
- end;
-
- end; {with}
- end; { generate }
-
-
- procedure address; { compute address of each label }
-
- begin
- if codpnt^.labeln <> 0
- then tab[codpnt^.labeln].val := loc;
- generate; { advance location counter }
- codpnt^.errn := errn;
- codpnt := codpnt^.next;
- end;
-
-
- procedure parse_line; { scan source and build intermediate code }
-
- var
- s: line; { source line }
- p: integer; { index into s }
- m: idtype; { mnemonic opcode }
- labeln: integer;
- temp: line;
- id: idtype; { identifier }
- preventry: cptr; { points to previous line of intermediate code }
-
- label nocode;
-
- function more: boolean; { any more characters on this line? }
- begin
- more := p <= length(s);
- end;
-
- procedure skipblank;
- begin
- while more and (s[p] = ' ') do
- p := p+1;
- end;
-
- function alpha: boolean;
- begin
- alpha := more and (s[p] in ['a'..'z','A'..'Z']);
- end;
-
- function digit: boolean;
- begin
- digit := more and (s[p] in ['0'..'9']);
- end;
-
- function peek(aset: charset): boolean; { is next character in aset? }
- begin
- if more and (s[p] in aset) then peek := true else peek := false;
- end;
-
- function test(c: char): boolean; { is the next character c? }
- begin { if so, scan over it }
- if more and (upcase(s[p]) = c)
- then begin
- p := p+1; skipblank;
- test := true
- end
- else test := false
- end;
-
- procedure getid; { found an alpha }
- begin { get rest of identifier }
- id := '';
- while alpha or digit or peek(['_']) do begin
- if length(id) < 20
- then id := id + s[p] { return it in id }
- else error(14);
- p := p+1;
- end;
- skipblank;
- end;
-
- procedure enter(symbol: idtype; var m: integer);
- { make entry in symbol table }
- begin
- if search(symbol)
- then error(15)
- else if tcnt = tsize then begin
- writeln; writeln('Assembly Aborted -- Symbol Table Full');
- close(src); close(targ);
- halt; end
- else begin
- tcnt := tcnt+1;
- tab[tcnt].id := stupcase(symbol);
- tab[tcnt].val := -1;
- m := tcnt;
- end;
- end;
-
- function code: boolean; { found an id }
- { is it an opcode? }
- begin
- op := nul;
- m := stupcase(id);
- repeat { if so, return it in op }
- op := succ(op)
- until (mn[op] = m) or (op = last);
- if op in [rep,repe,repz,repne,repnz] then begin
- if repx <> nul then error(13);
- repx := op; { REP prefix }
- if alpha then begin { look for another opcode }
- getid;
- code := code; end
- else error(4);
- end
- else if op=lock then begin
- if lockx then error(13);
- lockx := true; { LOCK prefix }
- if alpha then begin { look for another opcode }
- getid;
- code := code; end
- else error(4);
- end
- else if (op > valid) and (op <> last) then error(18)
- else if op <> last then begin
- code := true;
- if (repx<>nul) and not (op in [movs,cmps,scas,lods,stos]) then error(4);
- end
- else begin code := false; op := nul; end;
- end; { code }
-
- procedure getoperand(var opr: attr); { scan an operand }
- { determine its attributes }
- var r: regs;
-
- label gotid;
-
- procedure makebyte; { it's a byte }
- begin
- if opr.isword then error(3) else opr.isbyte := true;
- end;
-
- procedure makeword; { it's a word }
- begin
- if opr.isbyte then error(3) else opr.isword := true;
- end;
-
- procedure getnum; { scan a numeric literal }
-
- var code: integer;
- minus: boolean;
-
- procedure gethex; { scan a hexadecimal literal }
- begin
- if id = '-' then minus := true;
- id := '$'; p := p+1;
- while more and (digit or (upcase(s[p]) in ['A','B','C','D','E','F']))
- do begin
- id := id + s[p]; { return it in id }
- p := p+1;
- end;
- if id = '$' then error(2);
- end;
-
- begin
- id := ''; minus := false;
- if test('+') then;
- if test('-') then id := '-';
- if peek(['$'])
- then gethex { hex }
- else while digit do begin { decimal }
- id := id + s[p];
- p := p+1;
- end;
- if id = '' then error(2);
- with opr do begin
- val(id,value,code); { return value }
- if code<>0
- then if id='-32768'
- then value := $8000
- else error(9);
- if minus then value := -value
- end;
- if id[1] = '-' then delete(id,1,1);
- skipblank;
- end; { getnum }
-
-
- procedure getchar; { scan a character literal }
- begin
- with opr do begin
- p := p+1;
- value := ord(s[p]); p := p+1;
- if not test('''') then error(2)
- else begin
- isconst := true;
- isimmed := true;
- if not isword then isbyte := true;
- end;
- end; end;
-
- function testreg: boolean; { is id a register name? }
- begin
- r := firstreg;
- temp := stupcase(id);
- repeat
- r := succ(r) { if so, return register number in r }
- until (reg[r] = temp) or (r = lastreg);
- if r <> lastreg then testreg := true else testreg := false;
- end;
-
-
- begin {getoperand}
- with opr do begin
- isop := true;
- if not (alpha or digit or peek(['=','$','*','[','+','-','(','''']))
- then error(2)
- else begin
- if alpha then begin
- getid;
- if testreg and (r in [ds,cs,ss,es]) and peek([':'])
- then begin { segment override prefix }
- if test(':') then;
- if override<>lastreg then error(13);
- override := r; end
- else goto gotid;
- end;
- if test('(') then begin { type modifier }
- if test('B') then makebyte
- else if test('W') then makeword
- else if test('S') then isshort := true
- else if test('N') then isnear := true
- else if test('F') then isfar := true
- else error(6);
- if not test(')') then error(6);
- end;
- if test('=') then isimmed := true;
- if test('[')
- then begin { base or index register }
- if isimmed then error(2);
- isaddr := true;
- getid;
- if testreg
- then begin
- if not test(']') then error(6);
- if r in [bx,bp]
- then begin { base register }
- isbase := true; isop := true;
- base := r;
- if test('[')
- then begin
- getid;
- if testreg
- then begin
- if not test(']') then error(6);
- if r in [si,di]
- then begin { and index register }
- isidx := true;
- idx := r;
- end
- else error(8)
- end
- else error(5)
- end
- end
- else if r in [si,di]
- then begin { index register }
- isidx := true;
- idx := r;
- end
- else error(8);
- end
- else error(5)
- end;
- if alpha
- then begin { identifier }
- getid;
- gotid: if testreg
- then begin { it's a register }
- if r in [ds,ss,cs,es]
- then issreg := true
- else isreg := true;
- if r in [ax,bx,cx,dx,sp,bp,si,di,ds,ss,cs,es]
- then makeword;
- if r in [ah,bh,ch,dh,al,bl,cl,dl]
- then makebyte;
- if isimmed then error(2);
- rg := r;
- end
- else begin { it's a variable or label id }
- isaddr := not isimmed;
- isid := true;
- ident := id;
- if isimmed then makeword;
- end;
- end {alpha}
- else if digit or peek(['$','+','-'])
- then begin { numeric literal }
- getnum;
- isaddr := not isimmed;
- isconst := true;
- if isimmed
- then if (value <= 255) and (value >= -128) and not isword
- then makebyte
- else makeword;
- end
- else if test('*')
- then begin { location counter reference }
- ident := '*';
- isaddr := not isimmed;
- isid := true;
- if isimmed then makeword;
- if test('+') then ident := '*+';
- if test('-') then ident := '*-';
- if ident<>'*' then begin
- if not peek(['$','0'..'9']) then error(9);
- getnum;
- ident := ident + id;
- end;
- end
- else if peek(['''']) then getchar; { character literal }
- if isbase and (base=bp) and not isidx and not (isid or isconst)
- then begin
- isconst := true; value := 0;
- ident := '$00';
- end;
- end;
- if isimmed and not (isid or isconst) then error(6);
- end; {with}
- skipblank;
- end; {getoperand}
-
-
- begin { parse_line }
- errn := 0; labeln := 0;
- op := nul; repx := nul; lockx := false; override := lastreg;
- with op1 do begin
- isop := false; isaddr := false;
- isid := false; isreg := false; issreg := false;
- isidx := false; isbase := false;
- isbyte := false; isword := false;
- isshort := false; isnear := false; isfar := false;
- isimmed := false; isconst := false;
- end;
- op2 := op1;
- readln(src,s); { read in a source line }
- for p := 1 to length(s) do
- if ord(s[p]) < 32 then s[p] := ' ';
- p := 1;
- if more
- then begin
- skipblank;
- if alpha then begin
- getid;
- if test(':') then begin { label }
- enter(id,labeln);
- if alpha
- then getid
- else goto nocode;
- end;
- if code { opcode }
- then begin
- if more and not peek([';'])
- then begin
- getoperand(op1); { first operand }
- if test(',')
- then begin
- if more
- then getoperand(op2) { second operand }
- else error(6);
- if more and not peek([';']) then error(6);
- end
- else if more and not peek([';']) then error(6);
- end
- end
- else error(4)
- end
- else
- nocode: if more and not peek([';']) then error(6);
- preventry := codpnt;
- if maxavail > sizeof(codrec) shr 4 +1
- then new(codpnt) { create new line of intermediate code }
- else begin
- writeln; writeln('Assembly Aborted -- Out of Memory');
- close(src); close(targ); halt; end;
- if firstentry = nil then firstentry := codpnt;
- preventry^.next := codpnt; { and link it }
- codpnt^.next := nil;
- codpnt^.labeln := labeln;
- codpnt^.op := op; { enter the data }
- codpnt^.op1 := op1;
- codpnt^.op2 := op2;
- codpnt^.repx := repx;
- codpnt^.lockx := lockx;
- codpnt^.override := override;
- codpnt^.errn := errn;
- codpnt^.source := s;
- end;
- end; { parse_line }
-
-
- begin { main }
- writeln(' InLiner'); writeln;
- startup;
- init;
- atstart := true; ok := true;
- oldlen := 0; loc := 0; tcnt := 0;
-
- pass := 1; firstentry := nil;
- while not eof(src) do parse_line;
-
- pass := 2; codpnt := firstentry; loc := 0;
- while codpnt <> nil do address;
-
- pass := 3; codpnt := firstentry; loc := 0;
- while codpnt <> nil do generate;
-
- writeln;
- if ok then writeln('Assembly Successful')
- else writeln('Assembled with Errors');
- close(src); close(targ);
- end.